package Core.ip

import spinal.core._
import spinal.lib._

case class InstBufferSlave(WIDTH : Int) extends Bundle {
    val data = Bits(WIDTH bits)
}

case class InstBufferMaster(WIDTH : Int) extends Bundle {
    val data = Bits(WIDTH bits)
}

case class InstBufferPorts(WIDTH : Int) extends Bundle with IMasterSlave{
    val s_ports = Stream(InstBufferSlave(WIDTH))
    val m_ports = Stream(InstBufferMaster(WIDTH))

    override def asMaster(): Unit = {
        master(m_ports)
        slave(s_ports)
    }
}

case class InstBuffer(WIDTH : Int = 132, DEPTH : Int = 32, SIZE : Int = 4) extends Component{
    val ports = master(InstBufferPorts(WIDTH))
    val flush = in Bool()
    def PTR_WIDTH = log2Up(DEPTH)

    val read_ptr = Reg(UInt(PTR_WIDTH+1 bits)) init(0)
    val write_ptr= Reg(UInt(PTR_WIDTH+1 bits)) init(0)
    val read_addr = read_ptr(PTR_WIDTH-1 downto 2)
    val write_addr= write_ptr(PTR_WIDTH-1 downto 2)
    val fifo_empty = (read_ptr===write_ptr)
    val fifo_full = (read_addr===write_addr) && (read_ptr(PTR_WIDTH)=/=write_ptr(PTR_WIDTH))
    val fifo_ram_0 = Mem(Bits((WIDTH / SIZE) bits), DEPTH / SIZE)
    val fifo_ram_1 = Mem(Bits((WIDTH / SIZE) bits), DEPTH / SIZE)
    val fifo_ram_2 = Mem(Bits((WIDTH / SIZE) bits), DEPTH / SIZE)
    val fifo_ram_3 = Mem(Bits((WIDTH / SIZE) bits), DEPTH / SIZE)
    val instructions_in = ports.s_ports.payload
    val instructions_out = InstBufferMaster(WIDTH)

    when(flush){
        read_ptr := 0
    } .elsewhen(ports.m_ports.fire){
        read_ptr := read_ptr + SIZE
    }
    when(flush){
        write_ptr := 0
    } .elsewhen(ports.s_ports.fire){
        write_ptr := write_ptr + SIZE
        fifo_ram_0(write_addr) := instructions_in.data(32 downto 0)
        fifo_ram_1(write_addr) := instructions_in.data(65 downto 33)
        fifo_ram_2(write_addr) := instructions_in.data(98 downto 66)
        fifo_ram_3(write_addr) := instructions_in.data(131 downto 99)
    }

    instructions_out.data := fifo_ram_3(read_addr) ## fifo_ram_2(read_addr) ## fifo_ram_1(read_addr) ## fifo_ram_0(read_addr)

    ports.s_ports.ready := !fifo_full
    ports.m_ports.valid := !fifo_empty
    ports.m_ports.payload := instructions_out

}

object InstBuffer extends App {
    SpinalVerilog(InstBuffer(132, 32, 4))
}